package math;

import static math.math.*;

public class vecN
{
    
    public double[] m;
    
    
    public vecN(int aSize)
    {
        this.m = new double[aSize];
    }
    
    public vecN copy()
    {
        vecN copy = new vecN(this.m.length);
        for (int i = 0; i < this.m.length; i++)
        {
            copy.m[i] = this.m[i];
        }
        return copy;
    }
    
    
    public final void set(vecN other)
    {
        assertSizeEq(this, other);
        for (int i = 0; i < this.m.length; i++)
        {
            this.m[i] = other.m[i];
        }
    }
    
    public void set(int index, double value)
    {
        this.m[index] = value;
    }
    
    public void add(double term)
    {
        for (int i = 0; i < this.m.length; i++)
        {
            this.m[i] += term;
        }
    }
    
    public void add(vecN term)
    {
        assertSizeEq(this, term);
        for (int i = 0; i < this.m.length; i++)
        {
            this.m[i] += term.m[i];
        }
    }
    
    public void sub(double term)
    {
        this.add(-term);
    }
    
    public void sub(vecN term)
    {
        assertSizeEq(this, term);
        for (int i = 0; i < this.m.length; i++)
        {
            this.m[i] -= term.m[i];
        }
    }
    
    public void mul(double factor)
    {
        for (int i = 0; i < this.m.length; i++)
        {
            this.m[i] *= factor;
        }
    }
    
    public void mul(vecN factor)
    {
        assertSizeEq(this, factor);
        for (int i = 0; i < this.m.length; i++)
        {
            this.m[i] *= factor.m[i];
        }
    }
    
    public void div(double factor)
    {
        this.mul(1.0 / factor);
    }
    
    public void div(vecN factor)
    {
        assertSizeEq(this, factor);
        for (int i = 0; i < this.m.length; i++)
        {
            this.m[i] /= factor.m[i];
        }
    }
    
    public void addmul(double factor0, double factor1) // Xi += factor0 * factor1.
    {
        this.add(factor0 * factor1);
    }
    
    public void addmul(double factor0, vecN factor1)
    {
        assertSizeEq(this, factor1);
        for (int i = 0; i < this.m.length; i++)
        {
            this.m[i] += factor0 * factor1.m[i];
        }
    }
    
    public void addmul(vecN factor0, double factor1)
    {
        this.addmul(factor1, factor0);
    }
    
    public void addmul(vecN factor0, vecN factor1)
    {
        assertSizeEq(this, factor0);
        assertSizeEq(this, factor1);
        for (int i = 0; i < this.m.length; i++)
        {
            this.m[i] += factor0.m[i] * factor1.m[i];
        }
    }
    
    public void norm()
    {
        this.mul(rlength(this));
    }
    
    
    public double get(int index)
    {
        return this.m[index];
    }
    
    
    private static void assertSizeEq(vecN a, vecN b)
    {
        assert(a.m.length == b.m.length);
    }
    
    
    public static double dot(vecN a, vecN b)
    {
        assertSizeEq(a, b);
        double dotProd = 0.0;
        for (int i = 0; i < a.m.length; i++)
        {
            dotProd += a.m[i] * b.m[i];
        }
        return dotProd;
    }
    
    public static double lengthSq(vecN a)
    {
        return dot(a, a);
    }
    
    public static double length(vecN a)
    {
        return sqrt(lengthSq(a));
    }
    
    public static double rlength(vecN a)
    {
        return rsqrt(lengthSq(a));
    }
    
    
    public static vecN add(vecN a, vecN b)
    {
        vecN add = a.copy();
        add.add(b);
        return add;
    }
    
    public static vecN sub(vecN a, vecN b)
    {
        vecN sub = a.copy();
        sub.sub(b);
        return sub;
    }
    
    public static vecN mul(vecN a, vecN b)
    {
        vecN mul = a.copy();
        mul.mul(b);
        return mul;
    }
    
    public static vecN div(vecN a, vecN b)
    {
        vecN div = a.copy();
        div.div(b);
        return div;
    }
    
}
